package ch.fhnw.kosmonauten.server.net;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import ch.fhnw.kosmonauten.net.ServerBussyMessage;
import ch.fhnw.kosmonauten.net.ServerClientAccepted;

/**
 * Diese Klasse started den Server Thread.
 * 
 * @author Fabian Siegrist
 * @author Oliver Freiermuth
 * @author Mikhail Onoufrienko
 * @author Risto Milani
 * @version $Revision: 97 $
 *          File name: $HeadURL: http://iching-kosmonauten.googlecode.com/svn/I-Ching/src/ch/fhnw/kosmonauten/server/net/MultiThreadedServer.java $
 *          Last modified:$Date: 2013-05-24 13:23:05 +0000 (Fri, 24 May 2013) $
 *          Last modified by: $Author: molotok.mihon@gmail.com $
 */

// class definition
public class MultiThreadedServer implements Runnable {

	// attributes
	private ServerSocket listener;
	private final int port;
	protected ExecutorService threadPool;
	protected final int maxConnections;
	protected int i;
	private boolean stopped;

	// constructor
	/**
	 * @param port
	 * @param maxConnections
	 */
	public MultiThreadedServer(int port, int maxConnections) {
		this.port = port;
		this.threadPool = Executors.newFixedThreadPool(maxConnections);
		this.maxConnections = maxConnections;
	}

	// methods
	public void end() {
		this.stopped = true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		this.i = 0;

		openSocked();
		while (!stopped) {
			Socket clientSocket = null;
			try {
				clientSocket = listener.accept();
				this.log("-------------------------------------");
				this.log("Connection Client accepted: "
						+ clientSocket.getRemoteSocketAddress());
				this.log("-------------------------------------");
				this.log("");
				if (i++ < maxConnections) {
					ObjectOutputStream serverOut = new ObjectOutputStream(
							clientSocket.getOutputStream());
					serverOut.writeObject(new ServerClientAccepted());
					this.threadPool.execute(new ClientWorker(this,
							clientSocket, serverOut));
				} else {
					ObjectOutputStream serverOut = new ObjectOutputStream(
							clientSocket.getOutputStream());
					serverOut.writeObject(new ServerBussyMessage());
					clientSocket.close();
					this.log("-------------------------------------");
					this.log("Connection Closed - Server full.");
					this.log("Connection Client : "
							+ clientSocket.getRemoteSocketAddress());
					this.log("-------------------------------------");
					this.log("");
				}
			} catch (IOException e) {
				if (stopped) {
					this.log("-------------------------------------");
					this.log("Faild to accept client, because server is stopped: "
							+ this.port);
					this.log("-------------------------------------");
					this.log("");
				} else {
					this.log("-------------------------------------");
					this.log("Faild to accept client: " + this.port);
					this.log("-------------------------------------");
					this.log("");
				}
				System.exit(-1);

			}
		}
		this.threadPool.shutdown();
	}

	/**
	 * 
	 */
	private void openSocked() {
		try {
			this.listener = new ServerSocket(this.port);
			this.log("-------------------------------------");
			this.log("Server started...");
			this.log("Listening on port: " + this.listener.getLocalPort());
			this.log("-------------------------------------");
			this.log("");
		} catch (IOException e) {
			this.log("-------------------------------------");
			this.log("Could not start Server...");
			this.log("Faild to assign port: " + this.port);
			this.log("-------------------------------------");
			this.log("");
			System.exit(-1);
		}
	}

	/**
	 * @param message
	 */
	public void log(String message) {
		System.out.println(new Date().toString() + " - " + message);
	}
}